import {
  isEmpty,
  isNull,
  PagedList,
  PageParams,
  DurationParams,
  isUndefined,
  numberToString,
  canUse,
  toString,
} from "mmb";
import * as messagepb from "../grpc-client/shared/message_pb";
import * as storepb from "../grpc-client/service/store_pb";
import {
  getBgsStoreOpSafely,
  getMetadata,
  getOptionsOfLight,
  wrapperGrpcError,
} from "../grpc-client/grpcAdapter";
import GrpcErrCode from "../grpc-client/grpcErrCode";
import {
  productGroupPbToObject,
  productWithDistributeInfoPbToObject,
  pbTimestampToJs,
  jsTimestampToPb,
} from "./helper";
import { getCache, setCache, makeEntityKey } from "../proxy/redisProxy";
import {
  Store,
  StoreWithStat,
  StoreWithStatAndAccount,
  StoreWithStatAndEndorsement,
  StoreAccountWithdrawApplyment,
  AggrStoreAccountLogsToInOutRet,
  OpenDistributeStoreParams,
  UpdatetoreInfoParams,
  CreateStoreWithdrawApplymentParams,
  StoreEndorsement,
  UpsertStoreEndorsementParams,
} from "./storeDeclareTypes";
import {
  ProductGroup,
  AccountLogInfo,
  ProductWithDistributeInfo,
} from "./declareTypes";
import WithdrawTarget from "../bg-enums/withdrawTarget";
import UserStratification from "../bg-enums/userStratification";

export const checkStoreOwner = async (userId: string, storeId: string) => {
  const k = makeStoreOwnerCacheKey(storeId);
  const cv = await getCache(k);
  let userIdAsStoreOwner = "";
  if (canUse(cv)) {
    console.log("checkStoreOwner use cached value");
    userIdAsStoreOwner = cv!;
    console.log(`userIdsAsfor ${storeId} storeOwner:`, userIdAsStoreOwner);
  } else {
    console.log("checkStoreOwner use query");
    const storeOwnerUserId = await getStoreOwner(storeId);
    if (!isEmpty(storeOwnerUserId)) {
      userIdAsStoreOwner = storeOwnerUserId;
      await setCache(k, userIdAsStoreOwner); // replay=>OK
    }
  }
  return userIdAsStoreOwner === userId;
};

/****************************************************************************************/
/*                                   store conv                                         */
/****************************************************************************************/
const makeStoreOwnerCacheKey = makeEntityKey("store_owner");

const storePbToObject = (pb: storepb.StoreEntity): Store => {
  return {
    storeId: toString(pb.getId()),
    name: pb.getName(),
    brandId: toString(pb.getBrandId()),
    intro: pb.getIntro(),
    pic: pb.getPic(),
    homepageBg: pb.getHomepageBg(),
    tel: pb.getTel(),
    opened: pbTimestampToJs(pb.getOpened()),
    distributorId: toString(pb.getDistributorId()),
  };
};

/****************************************************************************************/
/*                                   store cmd                                          */
/****************************************************************************************/

export const openDistributeStore = async ({
  name,
  brandId,
  intro,
  pic,
  distributorId,
  userId,
}: OpenDistributeStoreParams): Promise<Store> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.OpenDistributeStoreRequest();

  req.setName(name);
  req.setBrandId(Number(brandId));
  req.setIntro(intro);
  req.setPic(pic);
  req.setDistributorId(Number(distributorId));
  req.setUserId(Number(userId));

  return new Promise((resolve, reject) => {
    cl.openDistributeStore(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(storePbToObject(res.getStore()!));
      }
    );
  });
};

export const updateStoreInfo = async ({
  storeId,
  name,
  intro,
  pic,
  homepageBg,
  tel,
}: UpdatetoreInfoParams): Promise<Store> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.UpdateStoreInfoRequest();

  req.setId(Number(storeId));
  if (!isUndefined(name)) {
    req.setName(name);
  }
  if (!isUndefined(intro)) {
    req.setIntro(intro);
  }
  if (!isUndefined(pic)) {
    req.setPic(pic);
  }
  if (!isUndefined(homepageBg)) {
    req.setHomepageBg(homepageBg);
  }
  if (!isUndefined(tel)) {
    req.setTel(tel);
  }

  return new Promise((resolve, reject) => {
    cl.updateStoreInfo(req, getMetadata(), getOptionsOfLight(), (err, res) => {
      if (!isNull(err)) {
        reject(wrapperGrpcError(err.details));
        return;
      }
      const opRet = res.getOpRet()!;
      if (!opRet.getSuccess()) {
        reject(wrapperGrpcError(opRet.getMsg()));
        return;
      }
      resolve(storePbToObject(res.getStore()!));
    });
  });
};

/****************************************************************************************/
/*                                   store query                                        */
/****************************************************************************************/

export const getStore = async (storeId: string): Promise<Store> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.GetStoreRequest();

  req.setId(Number(storeId));

  return new Promise((resolve, reject) => {
    cl.getStore(req, getMetadata(), getOptionsOfLight(), (err, res) => {
      if (!isNull(err)) {
        reject(wrapperGrpcError(err.details));
        return;
      }
      const opRet = res.getOpRet()!;
      if (!opRet.getSuccess()) {
        reject(wrapperGrpcError(opRet.getMsg()));
        return;
      }
      resolve(storePbToObject(res.getStore()!));
    });
  });
};

export const getStoreOwner = async (storeId: string): Promise<string> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.GetStoreOwnerRequest();

  req.setId(Number(storeId));

  return new Promise((resolve, reject) => {
    cl.getStoreOwner(req, getMetadata(), getOptionsOfLight(), (err, res) => {
      if (!isNull(err)) {
        reject(wrapperGrpcError(err.details));
        return;
      }
      const opRet = res.getOpRet()!;
      if (!opRet.getSuccess()) {
        reject(wrapperGrpcError(opRet.getMsg()));
        return;
      }
      const storeOwnerUserId: string =
        res.getUserId() > 0 ? toString(res.getUserId()) : "";
      resolve(storeOwnerUserId);
    });
  });
};

/****************************************************************************************/
/*                                   store joins conv                                   */
/****************************************************************************************/

const storeWithStatPbToObject = (
  pb: storepb.StoreWithStatEntity
): StoreWithStat => {
  return {
    storeId: toString(pb.getId()),
    name: pb.getName(),
    brandId: toString(pb.getBrandId()),
    intro: pb.getIntro(),
    pic: pb.getPic(),
    homepageBg: pb.getHomepageBg(),
    tel: pb.getTel(),
    opened: pbTimestampToJs(pb.getOpened()),
    distributorId: toString(pb.getDistributorId()),
    // store_ext
    fansCount: pb.getFansCount(),
    publishActivityCount: pb.getPublishActivityCount(),
    spokesmanCount: pb.getSpokesmanCount(),
    endorseRequestToAuditCount: pb.getEndorseRequestToAuditCount(),
    orderCount: pb.getOrderCount(),
    saleAmount: pb.getSaleAmount(),
    refundAmount: pb.getRefundAmount(),
    distributeAmount: pb.getDistributeAmount(),
    orderBonus: pb.getOrderBonus(),
    refundOrderBonus: pb.getRefundOrderBonus(),
  };
};

const storeWithStatAndAccountPbToObject = (
  pb: storepb.StoreWithStatAndAccountEntity
): StoreWithStatAndAccount => {
  return {
    storeId: toString(pb.getId()),
    name: pb.getName(),
    brandId: toString(pb.getBrandId()),
    intro: pb.getIntro(),
    pic: pb.getPic(),
    homepageBg: pb.getHomepageBg(),
    tel: pb.getTel(),
    opened: pbTimestampToJs(pb.getOpened()),
    distributorId: toString(pb.getDistributorId()),
    // store_ext
    fansCount: pb.getFansCount(),
    publishActivityCount: pb.getPublishActivityCount(),
    spokesmanCount: pb.getSpokesmanCount(),
    endorseRequestToAuditCount: pb.getEndorseRequestToAuditCount(),
    orderCount: pb.getOrderCount(),
    saleAmount: pb.getSaleAmount(),
    refundAmount: pb.getRefundAmount(),
    distributeAmount: pb.getDistributeAmount(),
    orderBonus: pb.getOrderBonus(),
    refundOrderBonus: pb.getRefundOrderBonus(),
    // store_account
    availableAmount: pb.getAvailableAmount(),
    pendingAmount: pb.getPendingAmount(),
    withdrawingAmount: pb.getWithdrawingAmount(),
    withdrawedAmount: pb.getWithdrawedAmount(),
  };
};

const storeWithStatAndEndorsementPbToObject = (
  pb: storepb.StoreWithStatAndEndorsementEntity
): StoreWithStatAndEndorsement => {
  return {
    storeId: toString(pb.getId()),
    name: pb.getName(),
    brandId: toString(pb.getBrandId()),
    intro: pb.getIntro(),
    pic: pb.getPic(),
    homepageBg: pb.getHomepageBg(),
    tel: pb.getTel(),
    opened: pbTimestampToJs(pb.getOpened()),
    distributorId: toString(pb.getDistributorId()),
    // store_ext
    fansCount: pb.getFansCount(),
    publishActivityCount: pb.getPublishActivityCount(),
    spokesmanCount: pb.getSpokesmanCount(),
    endorseRequestToAuditCount: pb.getEndorseRequestToAuditCount(),
    orderCount: pb.getOrderCount(),
    saleAmount: pb.getSaleAmount(),
    refundAmount: pb.getRefundAmount(),
    distributeAmount: pb.getDistributeAmount(),
    orderBonus: pb.getOrderBonus(),
    refundOrderBonus: pb.getRefundOrderBonus(),
    // store_account
    isEndorsementEntranceDisplay: pb.getIsEndorsementEntranceDisplay(),
    endorseSpokesmanId: toString(pb.getEndorseSpokesmanId()),
  };
};

/****************************************************************************************/
/*                                   store joins query                                  */
/****************************************************************************************/

export const getStoreWithStat = async (
  storeId: string
): Promise<StoreWithStat> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.GetStoreWithStatRequest();

  req.setId(Number(storeId));

  return new Promise((resolve, reject) => {
    cl.getStoreWithStat(req, getMetadata(), getOptionsOfLight(), (err, res) => {
      if (!isNull(err)) {
        reject(wrapperGrpcError(err.details));
        return;
      }
      const opRet = res.getOpRet()!;
      if (!opRet.getSuccess()) {
        reject(wrapperGrpcError(opRet.getMsg()));
        return;
      }
      resolve(storeWithStatPbToObject(res.getStoreWithStat()!));
    });
  });
};

export const getStoreWithStatAndAccount = async (
  storeId: string
): Promise<StoreWithStatAndAccount> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.GetStoreWithStatAndAccountRequest();

  req.setId(Number(storeId));

  return new Promise((resolve, reject) => {
    cl.getStoreWithStatAndAccount(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(
          storeWithStatAndAccountPbToObject(res.getStoreWithStatAndAccount()!)
        );
      }
    );
  });
};

export const getStoreWithStatAndEndorsementForUser = async (
  storeId: string,
  userId: string
): Promise<StoreWithStatAndEndorsement> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.GetStoreWithStatAndEndorsementForUserRequest();

  req.setId(Number(storeId));
  req.setUserId(Number(userId));

  return new Promise((resolve, reject) => {
    cl.getStoreWithStatAndEndorsementForUser(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(
          storeWithStatAndEndorsementPbToObject(
            res.getStoreWithStatAndEndorsement()!
          )
        );
      }
    );
  });
};

export const listStoreWithStatByUserIDAsDistributor = async (
  userId: string,
  pager: PageParams
): Promise<PagedList<StoreWithStat>> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.ListStoreWithStatByUserIDAsDistributorRequest();

  req.setLimit(pager.limit);
  req.setOffset(pager.offset);
  req.setUserId(Number(userId));

  return new Promise((resolve, reject) => {
    cl.listStoreWithStatByUserIDAsDistributor(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: PagedList<StoreWithStat> = {
          total: 0,
          items: [],
        };
        ret.total = res.getTotal();
        ret.items = res.getStoreWithStatsList().map(storeWithStatPbToObject);
        resolve(ret);
      }
    );
  });
};

/****************************************************************************************/
/*                                   store_account_withdraw_applyment conv              */
/****************************************************************************************/

const storeAccountWithdrawApplymentPbToObject = (
  pb: storepb.StoreAccountWithdrawApplymentEntity
): StoreAccountWithdrawApplyment => {
  return {
    storeAccountWithdrawApplymentId: toString(pb.getId()),
    storeId: toString(pb.getStoreId()),
    target: pb.getTarget() as WithdrawTarget,
    applied: pbTimestampToJs(pb.getApplied())!,
    amount: pb.getAmount(),
    remark: pb.getRemark(),
    applyState: pb.getApplyState(),
  };
};

/****************************************************************************************/
/*                                   store_account_withdraw_applyment cmd               */
/****************************************************************************************/

export const createStoreAccountWithdrawApplyment = async ({
  storeId,
  target,
  amount,
  realName,
  idCardNo,
  remark,
}: CreateStoreWithdrawApplymentParams): Promise<StoreAccountWithdrawApplyment> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.CreateStoreAccountWithdrawApplymentRequest();

  req.setStoreId(Number(storeId));
  req.setTarget(target);
  req.setAmount(amount);
  req.setRealName(realName);
  req.setIdCardNo(idCardNo);
  req.setRemark(remark);

  return new Promise((resolve, reject) => {
    cl.createStoreAccountWithdrawApplyment(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(
          storeAccountWithdrawApplymentPbToObject(
            res.getStoreAccountWithdrawApplyment()!
          )
        );
      }
    );
  });
};

export const listStoreAccountWithdrawApplyments = async (
  storeId: string,
  pager: PageParams
): Promise<PagedList<StoreAccountWithdrawApplyment>> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.ListStoreAccountWithdrawApplymentsRequest();

  req.setLimit(pager.limit);
  req.setOffset(pager.offset);
  req.setStoreId(Number(storeId));

  return new Promise((resolve, reject) => {
    cl.listStoreAccountWithdrawApplyments(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: PagedList<StoreAccountWithdrawApplyment> = {
          total: 0,
          items: [],
        };
        ret.total = res.getTotal();
        ret.items = res
          .getStoreAccountWithdrawApplymentsList()
          .map(storeAccountWithdrawApplymentPbToObject);
        resolve(ret);
      }
    );
  });
};

/****************************************************************************************/
/*                                   store account log entity                           */
/****************************************************************************************/

const userAccountLogPbToObject = (
  pb: messagepb.AccountLogInfo
): AccountLogInfo => {
  return {
    id: `${pb.getId()}`,
    logged: pbTimestampToJs(pb.getLogged())!,
    category: pb.getCategory(),
    amount: pb.getAmount(),
    subject: pb.getSubject(),
    ext_info: pb.getExtInfo(),
  };
};

/****************************************************************************************/
/*                                   user account log query                             */
/****************************************************************************************/

export const listStoreAccountLogs = async (
  storeId: string,
  duration: DurationParams,
  pager: PageParams
): Promise<PagedList<AccountLogInfo>> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.ListStoreAccountLogsRequest();

  req.setLimit(pager.limit);
  req.setOffset(pager.offset);
  req.setTs(jsTimestampToPb(pager.ts));
  req.setBegin(jsTimestampToPb(duration.begin));
  req.setEnd(jsTimestampToPb(duration.end));
  req.setStoreId(Number(storeId));

  return new Promise((resolve, reject) => {
    cl.listStoreAccountLogs(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: PagedList<AccountLogInfo> = {
          total: 0,
          items: [],
        };
        ret.total = res.getTotal();
        ret.items = res.getAccountLogsList().map(userAccountLogPbToObject);
        resolve(ret);
      }
    );
  });
};

export const aggrStoreAccountLogsToInOut = async (
  storeId: string,
  duration: DurationParams
): Promise<AggrStoreAccountLogsToInOutRet> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.AggrStoreAccountLogsToInOutRequest();

  req.setBegin(jsTimestampToPb(duration.begin));
  req.setEnd(jsTimestampToPb(duration.end));
  req.setStoreId(Number(storeId));

  return new Promise((resolve, reject) => {
    cl.aggrStoreAccountLogsToInOut(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: AggrStoreAccountLogsToInOutRet = {
          income: res.getIncome(),
          outlay: res.getOutlay(),
        };
        resolve(ret);
      }
    );
  });
};

/****************************************************************************************/
/*                                   store product query                                */
/****************************************************************************************/

export const listStoreProductGroups = async (
  userId: string,
  storeId: string,
  pager: PageParams
): Promise<PagedList<ProductGroup>> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.ListStoreProductGroupsRequest();

  req.setLimit(pager.limit);
  req.setOffset(pager.offset);
  req.setUserId(Number(userId));
  req.setStoreId(Number(storeId));

  return new Promise((resolve, reject) => {
    cl.listStoreProductGroups(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: PagedList<ProductGroup> = {
          total: 0,
          items: [],
        };
        ret.total = res.getTotal();
        ret.items = res.getProductGroupsList().map(productGroupPbToObject);
        resolve(ret);
      }
    );
  });
};

export interface ListStoreProductWithDistributeInfosParam {
  userId: string;
  storeId: string;
  productGroupId?: string;
  keyword?: string;
}

export const listStoreProductWithDistributeInfos = async (
  {
    userId,
    storeId,
    productGroupId,
    keyword,
  }: ListStoreProductWithDistributeInfosParam,
  pager: PageParams
): Promise<PagedList<ProductWithDistributeInfo>> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.ListStoreProductWithDistributeInfosRequest();

  req.setLimit(pager.limit);
  req.setOffset(pager.offset);
  req.setTs(jsTimestampToPb(pager.ts));
  req.setUserId(Number(userId));
  req.setStoreId(Number(storeId));
  if (!isUndefined(productGroupId)) {
    req.setProductGroupId(Number(productGroupId));
  }
  if (!isUndefined(keyword)) {
    req.setKeyword(keyword);
  }

  return new Promise((resolve, reject) => {
    cl.listStoreProductWithDistributeInfos(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: PagedList<ProductWithDistributeInfo> = {
          total: 0,
          items: [],
        };
        ret.total = res.getTotal();
        ret.items = res
          .getProductWithDistributeInfosList()
          .map(productWithDistributeInfoPbToObject);
        resolve(ret);
      }
    );
  });
};

export interface listStoreProductWithDistributeInfosByProductIDsParam {
  userId: string;
  storeId: string;
  productIds: string[];
}

export const listStoreProductWithDistributeInfosByProductIDs = async (
  {
    userId,
    storeId,
    productIds,
  }: listStoreProductWithDistributeInfosByProductIDsParam,
  pager: PageParams
): Promise<ProductWithDistributeInfo[]> => {
  const cl = getBgsStoreOpSafely();
  const req =
    new storepb.ListStoreProductWithDistributeInfosByProductIDsRequest();

  req.setUserId(Number(userId));
  req.setStoreId(Number(storeId));
  req.setProductIdsList(productIds.map(Number));

  return new Promise((resolve, reject) => {
    cl.listStoreProductWithDistributeInfosByProductIDs(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: PagedList<ProductWithDistributeInfo> = {
          total: 0,
          items: [],
        };
        resolve(
          res
            .getProductWithDistributeInfosList()
            .map(productWithDistributeInfoPbToObject)
        );
      }
    );
  });
};

/****************************************************************************************/
/*                                   store_endorsement entity                           */
/****************************************************************************************/

export const getDefaultStoreEndorsement = (
  storeId: string
): StoreEndorsement => {
  const v = {
    storeId,
    isOpen: false,
    spokesmanTitle: "代言人",
    userStratified: UserStratification.ANYONE,
    needApplyInfo: false,
    isEntranceDisplay: false,
    isAutoApproved: false,
  };
  return v;
};

const storeEndorsementPbToObject = (
  pb: storepb.StoreEndorsementEntity
): StoreEndorsement => {
  return {
    storeId: toString(pb.getStoreId()),
    isOpen: pb.getIsOpen(),
    spokesmanTitle: pb.getSpokesmanTitle(),
    userStratified: pb.getUserStratified() as UserStratification,
    consumerLowerExpense: pb.getConsumerLowerExpense(),
    needApplyInfo: pb.getNeedApplyInfo(),
    applyInfoFields: pb.getApplyInfoFieldsList(),
    isEntranceDisplay: pb.getIsEntranceDisplay(),
    isAutoApproved: pb.getIsAutoApproved(),
  };
};

/****************************************************************************************/
/*                                   store_endorsement cmd                              */
/****************************************************************************************/

export const upsertStoreEndorsement = async ({
  storeId,
  isOpen,
  spokesmanTitle,
  userStratified,
  consumerLowerExpense,
  needApplyInfo,
  applyInfoFields,
  isEntranceDisplay,
  isAutoApproved,
}: UpsertStoreEndorsementParams): Promise<StoreEndorsement> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.UpsertStoreEndorsementRequest();

  req.setStoreId(Number(storeId));
  req.setIsOpen(isOpen);
  req.setSpokesmanTitle(spokesmanTitle);
  req.setUserStratified(userStratified);
  if (!isUndefined(consumerLowerExpense)) {
    req.setConsumerLowerExpense(consumerLowerExpense);
  }
  req.setNeedApplyInfo(needApplyInfo);
  if (!isUndefined(applyInfoFields)) {
    req.setApplyInfoFieldsList(applyInfoFields);
  }
  req.setIsEntranceDisplay(isEntranceDisplay);
  req.setIsAutoApproved(isAutoApproved);

  return new Promise((resolve, reject) => {
    cl.upsertStoreEndorsement(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(storeEndorsementPbToObject(res.getStoreEndorsement()!));
      }
    );
  });
};

/****************************************************************************************/
/*                                   store_endorsement query                            */
/****************************************************************************************/
export const getStoreEndorsementByStoreIDSafely = async (
  storeId: string
): Promise<StoreEndorsement> => {
  let v = await getStoreEndorsementByStoreID(storeId);
  if (isUndefined(v)) {
    v = getDefaultStoreEndorsement(storeId);
  }
  return v;
};
export const getStoreEndorsementByStoreID = async (
  storeId: string
): Promise<StoreEndorsement | undefined> => {
  const cl = getBgsStoreOpSafely();
  const req = new storepb.GetStoreEndorsementByStoreIDRequest();

  req.setStoreId(Number(storeId));

  return new Promise((resolve, reject) => {
    cl.getStoreEndorsementByStoreID(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          if (opRet.getCode() === GrpcErrCode.NOT_FOUND) {
            resolve(undefined);
            return;
          } else {
            reject(wrapperGrpcError(opRet.getMsg()));
            return;
          }
        }
        resolve(storeEndorsementPbToObject(res.getStoreEndorsement()!));
      }
    );
  });
};
